home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
TPLY30.ARJ
/
YREF.Y
< prev
next >
Wrap
Text File
|
1991-06-17
|
9KB
|
403 lines
/* YREF.Y: YREF cross reference utility, V1.1 5-1-91 AG
This is a sample Yacc program which produces complete cross reference
listings of Yacc source (.y) files. It is based on the same grammar
which has actually been used to implement Yacc itself, and thus might
also be useful for other utilities which have to parse Yacc sources.
The lexical analyzer for this program can be found in YREFLEX.L. The
units YRefTools and YRefTables supply the additional routines and data
structures used by the program. To compile the program, issue the
commands:
yacc yref
lex yreflex
tpc /m yref
The YREF program reads and parses a Yacc source file and produces
a listing of the source file (with linenumbers), followed by a cross
reference table of all (literal and nonterminal) identifiers used in the
grammar, in alphabetical order. For each symbol, YREF lists the type of
the symbol (if type tags are used), and the corresponding line numbers
where the symbol occurs. Line numbers marked with an asterisk denote
places where the corresponding symbol is "defined" (the lines where a
terminal is introduced in a %token definition, or a nonterminal appears
on the left-hand side of a rule). Symbols which do not have at least
one defining position are listed as "undefined symbols" at the end of
the list; this is useful to check Yacc grammars for "completeness."
YREF does not handle syntactic errors in the source file; it is
assumed that the input file is a syntactically correct Yacc program.
If a syntax error is encountered, YREF simply gives an error message
indicating the offending position, and exits with return code 1.
The command line syntax is as follows:
YREF yacc-file[.Y] [output-file[.REF]]
Default extensions .Y for the input, and .REF for the output file
are supplied automatically. If the output file name is ommitted,
it defaults to the name of the input file with new suffix .REF.
*/
%{
{$I-}
uses YaccLib, LexLib, Dos, YRefTools, YRefTables;
procedure yyerror ( msg : String );
begin
writeln(msg , ' in line ', yylineno, ' at or near `', yytext, '''');
end(*yyerror*);
var tag : Integer; (* type tag *)
symlineno : Integer; (* line number of last identifier *)
procedure scan ( var c : Char ); forward;
(* scan for nonempty character, skipping comments *)
procedure skip ( delim : String ); forward;
(* skip up to next occurrence of delim *)
procedure search ( delim : String ); forward;
(* like skip, but retain found delimiter, and handle embedded strings *)
%}
/* Tokens of the Yacc language:
Note the use of C_ID to distinguish identifiers which start a new rule.
This is necessary because the parser is limited to one-symbol lookahead
and hence could not determine whether an identifier is followed by a
colon, starting a new rule, or is simply just another nonterminal or
token identifier in the right-hand of a rule. Thus the lexical analyzer
performs the necessary lookahead, and returns C_ID if the identifier is
followed by a colon (skipping blanks and comments), and ID otherwise. */
%token
ID /* identifiers: {letter}{letter_or_digit}* */
C_ID /* identifier which forms left side of rule, i.e. is
followed by a colon */
LITERAL /* literals (strings enclosed in single or double quotes) */
NUMBER /* nonnegative integers: {digit}+ */
PTOKEN PLEFT PRIGHT PNONASSOC PTYPE PSTART PPREC
/* reserved words: PTOKEN=%token, etc. */
PP /* source sections separator %% */
LCURL /* curly braces: %{ and %} */
RCURL
',' ':' ';' '|' '{' '}' '<' '>' '='
/* literals */
%start grammar
%%
grammar : defs PP rules aux_procs
;
aux_procs : /* empty: aux_procs is optional */
| PP { yyaccept; }
;
defs : /* empty */
| defs def
;
def : PSTART ID
| LCURL { search('%}'); } RCURL
| PTOKEN tag token_list
| PLEFT tag token_list
| PRIGHT tag token_list
| PNONASSOC tag token_list
| PTYPE tag nonterm_list
| PTYPE tag
;
tag : /* empty: type tag is optional */
{ tag := 0; }
| '<' ID '>'
{ tag := $2; }
;
token_list : token_num
| token_list token_num
| token_list ',' token_num
;
token_num : LITERAL opt_num
| ID
{ add_ref($1, symlineno, true);
set_type($1, tag); }
opt_num
;
opt_num : /* empty */
| NUMBER
;
nonterm_list : nonterm
| nonterm_list nonterm
| nonterm_list ',' nonterm
;
nonterm : ID
{ add_ref($1, symlineno, false);
set_type($1, tag); }
;
rules : rule1
| LCURL { search('%}'); } RCURL rule1
/* rules section may be prefixed
with `local' Turbo Pascal
declarations */
| rules rule
;
rule1 : C_ID
{ add_ref($1, symlineno, true); }
':' body prec
;
rule : rule1
| '|' body prec
;
body : /* empty */
| body LITERAL
| body ID
{ add_ref($2, symlineno, false); }
| body action
;
action : '{' { search('}'); } '}'
| '=' { skip(';'); }
/* old language feature; code must be
single statement ending with `;' */
;
prec : /* empty */
| PPREC LITERAL opt_action
| PPREC ID
{ add_ref($2, symlineno, false); }
opt_action
| prec ';'
;
opt_action : /* empty */
| action
;
%%
procedure scan ( var c : Char );
label next;
const tab = #9; nl = #10;
begin
next:
c := get_char;
case c of
' ', tab, nl: goto next;
'/': begin
c := get_char;
if c='*' then
begin
skip('*/');
goto next;
end
else
begin
unget_char(c);
unget_char('/');
c := '/';
end;
end;
else unget_char(c);
end;
end(*scan*);
procedure skip ( delim : String );
var i, j : Integer; c : Char;
begin
i := 1;
while i<=length(delim) do
begin
c := get_char;
if c=delim[i] then
inc(i)
else if c<>#0 then
begin
for j := i-1 downto 2 do unget_char(delim[j]);
i := 1;
end
else
exit;
end;
end(*skip*);
procedure search ( delim : String );
var i, j : Integer; c : Char;
begin
i := 1;
while i<=length(delim) do
begin
c := get_char;
if c=delim[i] then
inc(i)
else if c<>#0 then
begin
for j := i-1 downto 2 do unget_char(delim[j]);
i := 1;
if c='''' then
(* skip string *)
skip('''');
end
else
exit;
end;
for i := length(delim) downto 1 do
unget_char(delim[i]);
end(*search*);
(* Lexical analyzer: *)
{$I YRefLex}
(* Main program: *)
function addExt ( filename, ext : String ) : String;
(* add default extension to filename *)
var d : DirStr; n : NameStr; e : ExtStr;
begin
fsplit(filename, d, n, e);
if e='' then e := '.'+ext;
addExt := d+n+e;
end(*addExt*);
function root ( filename : String ) : String;
(* return filename with extension stripped off *)
var d : DirStr; n : NameStr; e : ExtStr;
begin
fsplit(filename, d, n, e);
root := d+n;
end(*root*);
var result, lineno : Integer;
yfile, reffile, line : String;
begin
(* sign-on: *)
writeln('YREF Version 1.0 [Mar 91], Copyright (c) 1991 by Albert Graef');
(* parse command line: *)
case paramCount of
1 : begin
yfile := addExt(paramStr(1), 'y');
reffile := root(paramStr(1))+'.ref';
end;
2 : begin
yfile := addExt(paramStr(1), 'y');
reffile := addExt(paramStr(2), 'ref');
end;
else
begin
writeln('Usage: YREF yacc-file[.Y] [output-file[.REF]]');
halt(0);
end;
end;
(* open files: *)
assign(yyinput, yfile);
assign(yyoutput, reffile);
reset(yyinput);
if ioresult<>0 then
begin
writeln('cannot open file '+yfile);
halt(1);
end;
rewrite(yyoutput);
if ioresult<>0 then
begin
writeln('cannot open file '+reffile);
halt(1);
end;
(* produce numbered listing: *)
lineno := 1;
while not eof(yyinput) do
begin
readln(yyinput, line);
writeln(yyoutput, lineno:5, ': ', line);
inc(lineno);
end;
close(yyinput); reset(yyinput);
(* parse: *)
result := yyparse;
(* produce cross reference listing *)
if result=0 then ref_list;
(* close files: *)
close(yyinput); close(yyoutput);
if result>0 then erase(yyoutput);
(* terminate: *)
if (result=0) and (n_undef>0) then
writeln(n_undef, ' undefined symbol(s)');
halt(result);
end.